/*
 * SurfaceFeatureValueCalculator.h
 *
 *  Created on: Jan 7, 2011
 *      Author: Chengnian SUN
 */

#ifndef SURFACEFEATUREVALUECALCULATOR_H_
#define SURFACEFEATUREVALUECALCULATOR_H_

#include "AbstractFeatureValueCalculator.h"
#include <cmath>

#include "../report-model/MasterBugReport.h"
#include "../report-model/DuplicateBugReport.h"

class SurfaceFeatureValueCalculator: public AbstractFeatureValueCalculator {
public:

  enum EnumSurfaceFeatureType {

    VERSION,

    COMPONENT,

    SUB_COMPONENT,

    REPORT_TYPE,

    PRIORITY,

    SUMMARY_SEMANTIC,

    DESC_SEMANTIC
  };

private:
  EnumSurfaceFeatureType m_type;

public:

  static double compute_version_similarity(
      const AbstractBugReport& query_report,
      const AbstractBugReport& base_report) {
    int distance = query_report.get_version() - base_report.get_version();
    if (distance < 0) {
      distance = -distance;
    }
    assert(distance == distance);
    assert(distance >= 0);
    //		const double result = 1.0 / 1 + log(1.0 + distance);
    const float result = 1.0f / (1.0f + distance);
    assert(result == result);
    return result;
  }

  static double compute_component_similarity(
      const AbstractBugReport& query_report,
      const AbstractBugReport& base_report) {
    return (query_report.get_component() == base_report.get_component());
  }

  static double compute_sub_component_similarity(
      const AbstractBugReport& query_report,
      const AbstractBugReport& base_report) {
    return (query_report.get_sub_component() == base_report.get_sub_component());
  }

  static double compute_report_type_similarity(
      const AbstractBugReport& query_report,
      const AbstractBugReport& base_report) {
    return (query_report.get_report_type() == base_report.get_report_type());
  }

  static double compute_priority_similarity(
      const AbstractBugReport& query_report,
      const AbstractBugReport& base_report) {
//		double feature_value = query_report.get_priority() - base_report.get_priority();
//		if (feature_value < 0) {
//			feature_value = -feature_value;
//		}
//		return feature_value;

    int distance = query_report.get_priority() - base_report.get_priority();
    if (distance < 0) {
      distance = -distance;
    }
    assert(distance == distance);
    assert(distance >= 0);
    //		const double result = 1.0 / 1 + log(1.0 + distance);
    const float result = 1.0f / (1.0f + distance);
    assert(result == result);
    return result;
  }

    /**
       * 计算两个报告之间的 summary 语义 相似度
       * @param query_report
       * @param base_report
       * @return
       */
    static double compute_summary_semantic_similarity(const AbstractBugReport& query_report, const AbstractBugReport& base_report) {
        return get_cosine_similarity(query_report.get_summary_semantic(), base_report.get_summary_semantic());
    }

    /**
     * 计算两个报告之间的 description 语义 相似度
     * @param query_report
     * @param base_report
     * @return
     */
    static double compute_desc_semantic_similarity(const AbstractBugReport& query_report, const AbstractBugReport& base_report) {
        return get_cosine_similarity(query_report.get_desc_semantic(), base_report.get_desc_semantic());
    }

    // 应为要求语义相似度，及余弦相似度，所以要自己用C++实现计算函数
/**
 * 求向量的模长
 * @param vec
 * @return
 */
    static double getMold(const vector<double>& vec) {
        int n = vec.size();
        double sum = 0.0;
        for (int i = 0; i < n; i++) {
            sum += vec[i] * vec[i];
        }
        return sqrt(sum);
    }

/**
 * 计算两个向量的余弦相似度
 * 余弦相似度公式：cosine(a,b) = [(a_1 * b_1) + (a_2 * b_2) + ... + (a_n * b_n)] / (Module(a) * Module(b))
 * @param vec1
 * @param vec2
 * @return
 */
    static double get_cosine_similarity(const vector<double>& vec1, const vector<double>& vec2) {
        int n = vec1.size();
        // 两个向量的长度必须要相等
        assert(n == vec2.size());
        double tmp = 0.0;
        for (int i = 0; i < n; i++) {
            tmp += fabs(vec1[i] * vec2[i]);
        }

        double mod1 = getMold(vec1);
        double mod2 = getMold(vec2);
        if (mod1 == 0 || mod2 == 0) {
            return 0;
        }
        return tmp / (mod1 * mod2);
    }

  virtual double compute_feature_value(const AbstractBugReport& query,
      const AbstractBugReport& report);

  SurfaceFeatureValueCalculator(const ReportBuckets& report_buckets,
      EnumSurfaceFeatureType type);

  virtual ~SurfaceFeatureValueCalculator();
};

#endif /* SURFACEFEATUREVALUECALCULATOR_H_ */
